package com.slink.fxfk.common.dao.imp;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.HibernateCallback;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.stereotype.Repository;

import com.slink.fxfk.common.dao.CommonDao;

@Repository("commonDao")
public class CommonDaoImpl implements CommonDao {
	public static final String SQLINERT = "insert into {tableName}({columns}) values({vholds})";

	public static final String HQLUPDATE = "update {entityName} set {props} where {where}";

	public static final String HQLDEL = "delete {entityName} {where}";

	@Autowired
	private HibernateTemplate hibernateTemplate;

	public <T> T get(Class<T> clz, Serializable id) {
		return this.hibernateTemplate.get(clz, id);
	}

	/**
	 * 实体投影查询
	 * 
	 * @param idProperty
	 *            实体主键名
	 * @param id
	 *            主键值
	 * @param properties
	 *            查询的属性列
	 */
	@SuppressWarnings("unchecked")
	public <T> T getSection(Class<T> entityClass, Serializable id,
			String idProperty, Set<String> properties) {
		idProperty = idProperty == null ? "id" : idProperty;
		ProjectionList pl = Projections.projectionList();
		properties.add(idProperty);

		for (String prop : properties) {
			pl.add(Property.forName(prop).as(prop));
		}

		return (T) this.hibernateTemplate.getSessionFactory()
				.getCurrentSession().createCriteria(entityClass)
				.add(Restrictions.eq(idProperty, id)).setProjection(pl)
				.setResultTransformer(Transformers.aliasToBean(entityClass))
				.uniqueResult();
	}

	/**
	 * 保存实体
	 */
	public Serializable save(Object entity) {
		return this.hibernateTemplate.save(entity);
	}

	/**
	 * 保存实体部分属性
	 */
	public int saveSection(Object entity) {
		// 获取元数据
		AbstractEntityPersister classMetadata = (AbstractEntityPersister) this.hibernateTemplate
				.getSessionFactory().getClassMetadata(entity.getClass());

		// 要插入的列名部分
		StringBuilder columns = new StringBuilder();

		// 要插入的值部分命名形式
		StringBuilder vholds = new StringBuilder();

		// 属性值映射
		Map<String, Object> values = new HashMap<String, Object>();

		// 如果ID属性处理
		if (classMetadata.hasIdentifierProperty()) {
			columns.append(classMetadata.getIdentifierColumnNames()[0]);
			vholds.append(":" + classMetadata.getIdentifierPropertyName());
			values.put(classMetadata.getIdentifierPropertyName(),
					classMetadata.getIdentifier(entity));
		}

		// 非ID属性操作
		String[] propertyNames = classMetadata.getPropertyNames();
		for (String name : propertyNames) {
			Object value = classMetadata.getPropertyValue(entity, name);

			if (value != null) {
				columns.append(" ,"
						+ classMetadata.getPropertyColumnNames(name)[0]);
				vholds.append(" ,:" + name);
				values.put(name, value);
			}

		}

		// 无属性值插入异常
		if (values.size() == 0) {
			throw new RuntimeException("All properties are null,entity:"
					+ entity);
		}

		// 无ID属性删除最列名部分与值命名部分前面的逗号
		if (!classMetadata.hasIdentifierProperty()) {
			columns.delete(0, 2);
			vholds.delete(0, 2);
		}

		// 创建SQL插入
		String sql = SQLINERT
				.replaceAll("\\{tableName\\}", classMetadata.getTableName())
				.replaceAll("\\{columns\\}", columns.toString())
				.replaceAll("\\{vholds\\}", vholds.toString());
		return this.hibernateTemplate.getSessionFactory().getCurrentSession()
				.createSQLQuery(sql).setProperties(values).executeUpdate();
	}

	public void saveOrUpdate(Object entity) {
		this.hibernateTemplate.saveOrUpdate(entity);
	}

	public void saveOrUpdateSection(Object entity) {
		// 获取元数据
		AbstractEntityPersister classMetadata = (AbstractEntityPersister) this.hibernateTemplate
				.getSessionFactory().getClassMetadata(entity.getClass());

		// 获取标识属性与值
		String idProp = classMetadata.getIdentifierPropertyName();
		Serializable idValue = classMetadata.getIdentifier(entity);

		if (idProp == null || idValue == null) {
			throw new RuntimeException(
					"the identifier is null or there is not identifier!");
		}

		// 查询数据库是否有此标识记录
		Object tmp = this.hibernateTemplate.get(entity.getClass(), idValue);

		if (tmp == null) {
			this.saveSection(entity);
		} else {
			this.updateSection(entity);
		}
	}

	public void update(Object entity) {
		this.hibernateTemplate.update(entity);
	}

	/**
	 * 更新部分属性
	 */
	public int updateSection(Object entity) {
		// 获取元数据
		AbstractEntityPersister classMetadata = (AbstractEntityPersister) this.hibernateTemplate
				.getSessionFactory().getClassMetadata(entity.getClass());

		// 获取标识属性与值
		String idProp = classMetadata.getIdentifierPropertyName();
		Serializable idValue = classMetadata.getIdentifier(entity);

		if (idProp == null || idValue == null) {
			throw new RuntimeException(
					"the identifier is null or there is not identifier!");
		}

		// 实体名
		String entityName = classMetadata.getEntityName();

		// set部分
		StringBuilder props = new StringBuilder();

		// 属性值映射
		Map<String, Object> values = new HashMap<String, Object>();
		values.put(idProp, idValue);

		// 非ID属性操作
		String[] propertyNames = classMetadata.getPropertyNames();
		for (String name : propertyNames) {
			Object value = classMetadata.getPropertyValue(entity, name);

			if (value != null) {
				props.append(" ," + name + "=:" + name);
				values.put(name, value);
			}

		}

		if (values.size() < 2) {
			throw new RuntimeException("there is no property needed to update!");
		}

		props = props.delete(0, 2);

		String hql = HQLUPDATE.replace("{entityName}", entityName)
				.replace("{where}", idProp + "=:" + idProp)
				.replace("{props}", props);
		return this.hibernateTemplate.getSessionFactory().getCurrentSession()
				.createQuery(hql).setProperties(values).executeUpdate();
	}

	/**
	 * 删除部分
	 */
	public int deleteSection(Object entity) {
		// 获取元数据
		AbstractEntityPersister classMetadata = (AbstractEntityPersister) this.hibernateTemplate
				.getSessionFactory().getClassMetadata(entity.getClass());

		// 获取标识属性与值
		String idProp = classMetadata.getIdentifierPropertyName();
		Serializable idValue = classMetadata.getIdentifier(entity);

		// 实体名
		String entityName = classMetadata.getEntityName();

		// where部分
		StringBuilder where = new StringBuilder("where 1=1");

		if (idValue != null) {
			where.append(" and " + idProp + "=:" + idProp);
		}

		// 非ID属性操作
		String[] propertyNames = classMetadata.getPropertyNames();
		for (String name : propertyNames) {
			Object value = classMetadata.getPropertyValue(entity, name);

			if (value != null) {
				where.append(" and " + name + "=:" + name);
			}
		}

		String hql = HQLDEL.replace("{entityName}", entityName).replace(
				"{where}", where.toString());
		int i = this.hibernateTemplate.getSessionFactory().getCurrentSession()
				.createQuery(hql).setProperties(entity).executeUpdate();

		return i;
	}

	/**
	 * 样例查询
	 */
	public <T> List<T> findByExample(T exampleEntity) {
		return this.hibernateTemplate.findByExample(exampleEntity);
	}

	public <T> T uniqueByExample(T exampleEntity) {
		List<T> list = this.hibernateTemplate.findByExample(exampleEntity);

		if (list == null || list.size() == 0) {
			return null;
		} else {
			if (list.size() == 1) {
				return list.get(0);
			} else {
				throw new IllegalArgumentException("there are multi records!");
			}
		}
	}

	public <T> long getCountByExample(T exampleEntity) {
		long count = (Long) this.hibernateTemplate
				.getSessionFactory()
				.getCurrentSession()
				.createCriteria(exampleEntity.getClass())
				.setProjection(
						Projections.projectionList()
								.add(Projections.rowCount()))
				.add(Example.create(exampleEntity)).uniqueResult();

		return count;
	}

	/**
	 * 分页查询实体集
	 */
	public <T> List<T> listByExample(T exampleEntity, int pageNo, int pageSize) {
		pageNo = pageNo < 1 ? 1 : pageNo;
		pageSize = pageSize < 1 ? 1 : pageSize;
		return this.hibernateTemplate.findByExample(exampleEntity, (pageNo - 1)
				* pageSize, pageSize);
	}

	public HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

	/**
	 * 使用hql进行分页查询
	 * 
	 * @param hql
	 *            需要查询的hql语句
	 * @param offset
	 *            第一条记录索引
	 * @param pageSize
	 *            当前需要显示的记录数
	 * @return 当前页的所有记录
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List findByPage(final String hql, final int offset,
			final int pageSize) {
		List list = this.hibernateTemplate.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException {
				List list = session.createQuery(hql).setFirstResult(offset)
						.setMaxResults(pageSize).list();
				return list;
			}
		});

		return list;
	}

	/**
	 * 使用hql进行分页查询
	 * 
	 * @param hql
	 *            需要查询的hql语句
	 * @param value
	 *            如果hql有一个参数需要传入，value就是传入hql语句的参数
	 * @param offset
	 *            第一条记录索引
	 * @param pageSize
	 *            当前需要显示的记录数
	 * @return 当前页的所有记录
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List findByPage(final String hql, final Object value,
			final int offset, final int pageSize) {
		// 通过一个HibernateCallback对象来执行查询
		List list = getHibernateTemplate().execute(new HibernateCallback() {// 实现HibernateCallback接口必须实现的方法
					public Object doInHibernate(Session session)
							throws HibernateException {// 执行Hibernate分页查询
						List result = session.createQuery(hql)
								// 为hql语句传入参数
								.setParameter(0, value).setFirstResult(offset)
								.setMaxResults(pageSize).list();
						return result;
					}
				});
		return list;
	}

	/**
	 * 使用hql进行分页查询
	 * 
	 * @param hql
	 *            需要查询的hql语句
	 * @param values
	 *            如果hql有多个参数需要传入，values就是传入hql语句的参数数组
	 * @param offset
	 *            第一条记录索引
	 * @param pageSize
	 *            当前需要显示的记录数
	 * @return 当前页的所有记录
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List findByPage(final String hql, final Object[] values,
			final int offset, final int pageSize) {
		List list = this.hibernateTemplate.execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session)
					throws HibernateException {
				Query query = session.createQuery(hql);
				for (int i = 0; i < values.length; i++) {
					query.setParameter(i, values[i]);
				}
				List list = query.setFirstResult(offset)
						.setMaxResults(pageSize).list();
				return list;
			}
		});
		return list;
	}

	/**
	 * 
	 * @param entityClass
	 * @param propertyName
	 *            按这个属性排序
	 * @param isAsc
	 *            降序还是升序
	 * @param firstResult
	 * @param maxResults
	 * @param criterions
	 * @return
	 */
	@SuppressWarnings({ "unchecked" })
	public <T> List<T> findByCriteria(final Class<T> entityClass,
			final String propertyName, final boolean isAsc,
			final int firstResult, final int maxResults,
			final Criterion... criterions) {
		List<T> list = (List<T>) this.hibernateTemplate
				.execute(new HibernateCallback<T>() {

					@Override
					public T doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = session.createCriteria(entityClass);
						// 按属性条件查询
						for (Criterion c : criterions) {
							criteria.add(c);
						}
						// 按某个属性排序
						if (null != propertyName) {
							if (isAsc) {
								criteria.addOrder(Order.asc(propertyName));
							} else {
								criteria.addOrder(Order.desc(propertyName));
							}
						}
						// 用于分页查询
						if (0 != maxResults) {
							criteria.setFirstResult(firstResult);
							criteria.setMaxResults(maxResults);
						}
						List<T> list = criteria.list();
						return (T) list;
					}
				});
		return list;
	}

	/**
	 * 获取Query对象
	 * 
	 * @param queryStr
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Query getQuery(final String queryStr) {
		return (Query) this.hibernateTemplate.execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session)
					throws HibernateException {
				return session.createQuery(queryStr);
			}
		});
	}

}
